其他
VT虚拟化技术笔记
本文为看雪论坛优秀文章
看雪论坛作者ID:smallzhong_
VT技术笔记(part 1)
VT概述
VT执行的大体流程
检测是否支持VT(开锁)
IA32_VMX_CR0_FIXED0 寄存器中为1的位,在cr0中必须为1;
IA32_VMX_CR0_FIXED1 寄存器中为0的位,在cr0中必须为0;
IA32_VMX_CR4_FIXED0 寄存器中为1的位,在cr4中必须为1;
IA32_VMX_CR4_FIXED1 寄存器中为0的位,在cr4中必须为0。
//检测Bios是否开启VT
BOOLEAN VmxIsCheckSupportVTBIOS()
{
ULONG64 value = __readmsr(IA32_FEATURE_CONTROL);
return (value & 0x5) == 0x5;
}
//检测CPU是否支持VT
BOOLEAN VmxIsCheckSupportVTCPUID()
{
int cpuidinfo[4];
__cpuidex(cpuidinfo, 1, 0);
//CPUID 是否支持VT ecx.vmx第6位 如果为1,支持VT,否则不支持
return (cpuidinfo[2] >> 5) & 1;
}
//检测CR4VT是否开启,如果为1 代表已经开启过了,否则没有开启
BOOLEAN VmxIsCheckSupportVTCr4()
{
ULONG64 mcr4 = __readcr4();
//检测CR4 VT是否开启,cr4.vmxe如果第14位为1,那么VT已经被开启,否则可以开启
return ((mcr4 >> 13) & 1) == 0;
}
void checkVT()
{
if (VmxIsCheckSupportVTCPUID())
{
DbgPrintEx(77, 0, "[db]:VmxIsCheckSupportVTCPUID number = %d\r\n", KeGetCurrentProcessorNumber());
}
if (VmxIsCheckSupportVTBIOS())
{
DbgPrintEx(77, 0, "[db]:VmxIsCheckSupportVTBIOS number = %d\r\n", KeGetCurrentProcessorNumber());
}
if (VmxIsCheckSupportVTCr4())
{
DbgPrintEx(77, 0, "[db]:VmxIsCheckSupportVTCr4 number = %d\r\n", KeGetCurrentProcessorNumber());
}
}
vmxon(开柜门)
PHYSICAL_ADDRESS lowphys,heiPhy;
lowphys.QuadPart = 0;
heiPhy.QuadPart = -1;
pVcpu->VmxOnAddr = MmAllocateContiguousMemorySpecifyCache(PAGE_SIZE, lowphys, heiPhy, lowphys, MmCached);
ULONG64 vmxBasic = __readmsr(IA32_VMX_BASIC);
*(PULONG)pVcpu->VmxOnAddr = (ULONG)vmxBasic;
ULONG64 vcr00 = __readmsr(IA32_VMX_CR0_FIXED0);
ULONG64 vcr01 = __readmsr(IA32_VMX_CR0_FIXED1);
ULONG64 vcr04 = __readmsr(IA32_VMX_CR4_FIXED0);
ULONG64 vcr14 = __readmsr(IA32_VMX_CR4_FIXED1);
ULONG64 mcr4 = __readcr4();
ULONG64 mcr0 = __readcr0();
mcr4 |= vcr04;
mcr4 &= vcr14;
mcr0 |= vcr00;
mcr0 &= vcr01;
__writecr0(mcr0);
__writecr4(mcr4);
int error = __vmx_on(&pVcpu->VmxOnAddrPhys.QuadPart);
申请vmcs内存并vmclear(拔电源)
__vmx_vmclear(&pVcpu->VmxcsAddrPhys.QuadPart);
vmptrload(选中机器)
__vmx_vmptrld(&pVcpu->VmxcsAddrPhys.QuadPart);
设置vmcs(装机)
vm execution control
vm exit control
vm entry control
参考文献
intel白皮书
邓志《处理器虚拟化技术》
B站周壑VT教学视频
VT虚拟化技术笔记(part 2)
设置vmcs控制区的guest和host区域
vm execution control
vm exit control
vm entry control
vmxinit函数框架的搭建以及guest rip,rsp的获取方法
int VmxInitVmOn()
{
PVMXCPUPCB pVcpu = VmxGetCurrentCPUPCB();
PHYSICAL_ADDRESS lowphys,heiPhy;
lowphys.QuadPart = 0;
heiPhy.QuadPart = -1;
pVcpu->VmxOnAddr = MmAllocateContiguousMemorySpecifyCache(PAGE_SIZE, lowphys, heiPhy, lowphys, MmCached);
if (!pVcpu->VmxOnAddr)
{
//申请内存失败
return -1;
}
memset(pVcpu->VmxOnAddr, 0, PAGE_SIZE);
pVcpu->VmxOnAddrPhys = MmGetPhysicalAddress(pVcpu->VmxOnAddr);
//填充ID
ULONG64 vmxBasic = __readmsr(IA32_VMX_BASIC);
*(PULONG)pVcpu->VmxOnAddr = (ULONG)vmxBasic;
//CR0,CR4
ULONG64 vcr00 = __readmsr(IA32_VMX_CR0_FIXED0);
ULONG64 vcr01 = __readmsr(IA32_VMX_CR0_FIXED1);
ULONG64 vcr04 = __readmsr(IA32_VMX_CR4_FIXED0);
ULONG64 vcr14 = __readmsr(IA32_VMX_CR4_FIXED1);
ULONG64 mcr4 = __readcr4();
ULONG64 mcr0 = __readcr0();
mcr4 |= vcr04;
mcr4 &= vcr14;
mcr0 |= vcr00;
mcr0 &= vcr01;
//
__writecr0(mcr0);
__writecr4(mcr4);
int error = __vmx_on(&pVcpu->VmxOnAddrPhys.QuadPart);
if (error)
{
//释放内存,重置CR4
mcr4 &= ~vcr04;
__writecr4(mcr4);
MmFreeContiguousMemorySpecifyCache(pVcpu->VmxOnAddr, PAGE_SIZE, MmCached);
pVcpu->VmxOnAddr = NULL;
pVcpu->VmxOnAddrPhys.QuadPart = 0;
}
return error;
}
PULONG64 retAddr = (PULONG64)_AddressOfReturnAddress();
ULONG64 guestEsp = retAddr + 1;
ULONG64 guestEip = *retAddr;
int VmxInit(ULONG64 hostEip)
{
PVMXCPUPCB pVcpu = VmxGetCurrentCPUPCB();
pVcpu->cpuNumber = KeGetCurrentProcessorNumberEx(NULL);
PULONG64 retAddr = (PULONG64)_AddressOfReturnAddress();
ULONG64 guestEsp = retAddr + 1;
ULONG64 guestEip = *retAddr;
int error = VmxInitVmOn();
if (error)
{
DbgPrintEx(77, 0, "[db]:vmon 初始化失败 error = %d,cpunumber %d\r\n", error, pVcpu->cpuNumber);
return error;
}
error = VmxInitVmcs(guestEip, guestEsp, hostEip);
if (error)
{
DbgPrintEx(77, 0, "[db]:vmcs 初始化失败 error = %d,cpunumber %d\r\n", error, pVcpu->cpuNumber);
VmxDestory();
return error;
}
return 0;
}
设置vmcs字段的vmxinitvmcs函数解析
int VmxInitVmcs(ULONG64 GuestEip,ULONG64 GuestEsp, ULONG64 hostEip)
{
PVMXCPUPCB pVcpu = VmxGetCurrentCPUPCB();
PHYSICAL_ADDRESS lowphys, heiPhy;
lowphys.QuadPart = 0;
heiPhy.QuadPart = -1;
pVcpu->VmxcsAddr = MmAllocateContiguousMemorySpecifyCache(PAGE_SIZE, lowphys, heiPhy, lowphys, MmCached);
if (!pVcpu->VmxcsAddr)
{
//申请内存失败
return -1;
}
memset(pVcpu->VmxcsAddr, 0, PAGE_SIZE);
pVcpu->VmxcsAddrPhys = MmGetPhysicalAddress(pVcpu->VmxcsAddr);
pVcpu->VmxHostStackTop = MmAllocateContiguousMemorySpecifyCache(PAGE_SIZE * 36, lowphys, heiPhy, lowphys, MmCached);
if (!pVcpu->VmxHostStackTop)
{
//申请内存失败
return -1;
}
memset(pVcpu->VmxHostStackTop, 0, PAGE_SIZE * 36);
pVcpu->VmxHostStackBase = (ULONG64)pVcpu->VmxHostStackTop + PAGE_SIZE * 36 - 0x200;
//填充ID
ULONG64 vmxBasic = __readmsr(IA32_VMX_BASIC);
*(PULONG)pVcpu->VmxcsAddr = (ULONG)vmxBasic;
//加载VMCS
__vmx_vmclear(&pVcpu->VmxcsAddrPhys.QuadPart);
__vmx_vmptrld(&pVcpu->VmxcsAddrPhys.QuadPart);
VmxInitGuest(GuestEip, GuestEsp);
VmxInitHost(hostEip);
}
GDT表项属性的分离与填充(es cs ss ds fs gs ldtr)
void fillGdtDataItem(int index,short selector)
{
GdtTable gdtTable = {0};
AsmGetGdtTable(&gdtTable);
selector &= 0xFFF8;
ULONG limit = __segmentlimit(selector);
PULONG item = (PULONG)(gdtTable.Base + selector);
LARGE_INTEGER itemBase = {0};
itemBase.LowPart = (*item & 0xFFFF0000) >> 16;
item += 1;
itemBase.LowPart |= (*item & 0xFF000000) | ((*item & 0xFF) << 16);
//属性
ULONG attr = (*item & 0x00F0FF00) >> 8;
if (selector == 0)
{
attr |= 1 << 16;
}
__vmx_vmwrite(GUEST_ES_BASE + index * 2, itemBase.QuadPart);
__vmx_vmwrite(GUEST_ES_LIMIT + index * 2, limit);
__vmx_vmwrite(GUEST_ES_AR_BYTES + index * 2, attr);
__vmx_vmwrite(GUEST_ES_SELECTOR + index * 2, selector);
}
tr寄存器gdt表项的填充
GdtTable gdtTable = { 0 };
AsmGetGdtTable(&gdtTable);
ULONG trSelector = AsmReadTR();
trSelector &= 0xFFF8;
ULONG trlimit = __segmentlimit(trSelector);
LARGE_INTEGER trBase = {0};
PULONG trItem = (PULONG)(gdtTable.Base + trSelector);
//读TR
trBase.LowPart = ((trItem[0] >> 16) & 0xFFFF) | ((trItem[1] & 0xFF) << 16) | ((trItem[1] & 0xFF000000));
trBase.HighPart = trItem[2];
//属性
ULONG attr = (trItem[1] & 0x00F0FF00) >> 8;
__vmx_vmwrite(GUEST_TR_BASE, trBase.QuadPart);
__vmx_vmwrite(GUEST_TR_LIMIT, trlimit);
__vmx_vmwrite(GUEST_TR_AR_BYTES, attr);
__vmx_vmwrite(GUEST_TR_SELECTOR, trSelector);
guest区域中其他寄存器的填充
__vmx_vmwrite(GUEST_CR0, __readcr0());
__vmx_vmwrite(GUEST_CR4, __readcr4());
__vmx_vmwrite(GUEST_CR3, __readcr3());
__vmx_vmwrite(GUEST_DR7, __readdr(7));
__vmx_vmwrite(GUEST_RFLAGS, __readeflags());
__vmx_vmwrite(GUEST_RSP, GuestEsp);
__vmx_vmwrite(GUEST_RIP, GuestEip);
__vmx_vmwrite(VMCS_LINK_POINTER, -1);
__vmx_vmwrite(GUEST_IA32_DEBUGCTL, __readmsr(IA32_MSR_DEBUGCTL));
__vmx_vmwrite(GUEST_IA32_PAT, __readmsr(IA32_MSR_PAT));
__vmx_vmwrite(GUEST_IA32_EFER, __readmsr(IA32_MSR_EFER));
__vmx_vmwrite(GUEST_FS_BASE, __readmsr(IA32_FS_BASE));
__vmx_vmwrite(GUEST_GS_BASE, __readmsr(IA32_GS_BASE));
__vmx_vmwrite(GUEST_SYSENTER_CS, __readmsr(0x174));
__vmx_vmwrite(GUEST_SYSENTER_ESP, __readmsr(0x175));
__vmx_vmwrite(GUEST_SYSENTER_EIP, __readmsr(0x176));
//IDT GDT
GdtTable idtTable;
__sidt(&idtTable);
__vmx_vmwrite(GUEST_GDTR_BASE, gdtTable.Base);
__vmx_vmwrite(GUEST_GDTR_LIMIT, gdtTable.limit);
__vmx_vmwrite(GUEST_IDTR_LIMIT, idtTable.limit);
__vmx_vmwrite(GUEST_IDTR_BASE, idtTable.Base);
guest区域填充完整代码
void fillGdtDataItem(int index,short selector)
{
GdtTable gdtTable = {0};
AsmGetGdtTable(&gdtTable);
selector &= 0xFFF8;
ULONG limit = __segmentlimit(selector);
PULONG item = (PULONG)(gdtTable.Base + selector);
LARGE_INTEGER itemBase = {0};
itemBase.LowPart = (*item & 0xFFFF0000) >> 16;
item += 1;
itemBase.LowPart |= (*item & 0xFF000000) | ((*item & 0xFF) << 16);
//属性
ULONG attr = (*item & 0x00F0FF00) >> 8;
if (selector == 0)
{
attr |= 1 << 16;
}
__vmx_vmwrite(GUEST_ES_BASE + index * 2, itemBase.QuadPart);
__vmx_vmwrite(GUEST_ES_LIMIT + index * 2, limit);
__vmx_vmwrite(GUEST_ES_AR_BYTES + index * 2, attr);
__vmx_vmwrite(GUEST_ES_SELECTOR + index * 2, selector);
}
void VmxInitGuest(ULONG64 GuestEip, ULONG64 GuestEsp)
{
fillGdtDataItem(0, AsmReadES());
fillGdtDataItem(1, AsmReadCS());
fillGdtDataItem(2, AsmReadSS());
fillGdtDataItem(3, AsmReadDS());
fillGdtDataItem(4, AsmReadFS());
fillGdtDataItem(5, AsmReadGS());
fillGdtDataItem(6, AsmReadLDTR());
GdtTable gdtTable = { 0 };
AsmGetGdtTable(&gdtTable);
ULONG trSelector = AsmReadTR();
trSelector &= 0xFFF8;
ULONG trlimit = __segmentlimit(trSelector);
LARGE_INTEGER trBase = {0};
PULONG trItem = (PULONG)(gdtTable.Base + trSelector);
//读TR
trBase.LowPart = ((trItem[0] >> 16) & 0xFFFF) | ((trItem[1] & 0xFF) << 16) | ((trItem[1] & 0xFF000000));
trBase.HighPart = trItem[2];
//属性
ULONG attr = (trItem[1] & 0x00F0FF00) >> 8;
__vmx_vmwrite(GUEST_TR_BASE, trBase.QuadPart);
__vmx_vmwrite(GUEST_TR_LIMIT, trlimit);
__vmx_vmwrite(GUEST_TR_AR_BYTES, attr);
__vmx_vmwrite(GUEST_TR_SELECTOR, trSelector);
__vmx_vmwrite(GUEST_CR0, __readcr0());
__vmx_vmwrite(GUEST_CR4, __readcr4());
__vmx_vmwrite(GUEST_CR3, __readcr3());
__vmx_vmwrite(GUEST_DR7, __readdr(7));
__vmx_vmwrite(GUEST_RFLAGS, __readeflags());
__vmx_vmwrite(GUEST_RSP, GuestEsp);
__vmx_vmwrite(GUEST_RIP, GuestEip);
__vmx_vmwrite(VMCS_LINK_POINTER, -1);
__vmx_vmwrite(GUEST_IA32_DEBUGCTL, __readmsr(IA32_MSR_DEBUGCTL));
__vmx_vmwrite(GUEST_IA32_PAT, __readmsr(IA32_MSR_PAT));
__vmx_vmwrite(GUEST_IA32_EFER, __readmsr(IA32_MSR_EFER));
__vmx_vmwrite(GUEST_FS_BASE, __readmsr(IA32_FS_BASE));
__vmx_vmwrite(GUEST_GS_BASE, __readmsr(IA32_GS_BASE));
__vmx_vmwrite(GUEST_SYSENTER_CS, __readmsr(0x174));
__vmx_vmwrite(GUEST_SYSENTER_ESP, __readmsr(0x175));
__vmx_vmwrite(GUEST_SYSENTER_EIP, __readmsr(0x176));
//IDT GDT
GdtTable idtTable;
__sidt(&idtTable);
__vmx_vmwrite(GUEST_GDTR_BASE, gdtTable.Base);
__vmx_vmwrite(GUEST_GDTR_LIMIT, gdtTable.limit);
__vmx_vmwrite(GUEST_IDTR_LIMIT, idtTable.limit);
__vmx_vmwrite(GUEST_IDTR_BASE, idtTable.Base);
}
host区域填充
void VmxInitHost(ULONG64 HostEip)
{
GdtTable gdtTable = { 0 };
AsmGetGdtTable(&gdtTable);
PVMXCPUPCB pVcpu = VmxGetCurrentCPUPCB();
ULONG trSelector = AsmReadTR();
trSelector &= 0xFFF8;
LARGE_INTEGER trBase = { 0 };
PULONG trItem = (PULONG)(gdtTable.Base + trSelector);
//读TR
trBase.LowPart = ((trItem[0] >> 16) & 0xFFFF) | ((trItem[1] & 0xFF) << 16) | ((trItem[1] & 0xFF000000));
trBase.HighPart = trItem[2];
//属性
__vmx_vmwrite(HOST_TR_BASE, trBase.QuadPart);
__vmx_vmwrite(HOST_TR_SELECTOR, trSelector);
__vmx_vmwrite(HOST_ES_SELECTOR, AsmReadES() & 0xfff8);
__vmx_vmwrite(HOST_CS_SELECTOR, AsmReadCS() & 0xfff8);
__vmx_vmwrite(HOST_SS_SELECTOR, AsmReadSS() & 0xfff8);
__vmx_vmwrite(HOST_DS_SELECTOR, AsmReadDS() & 0xfff8);
__vmx_vmwrite(HOST_FS_SELECTOR, AsmReadFS() & 0xfff8);
__vmx_vmwrite(HOST_GS_SELECTOR, AsmReadGS() & 0xfff8);
__vmx_vmwrite(HOST_CR0, __readcr0());
__vmx_vmwrite(HOST_CR4, __readcr4());
__vmx_vmwrite(HOST_CR3, __readcr3());
__vmx_vmwrite(HOST_RSP, (ULONG64)pVcpu->VmxHostStackBase);
__vmx_vmwrite(HOST_RIP, HostEip);
__vmx_vmwrite(HOST_IA32_PAT, __readmsr(IA32_MSR_PAT));
__vmx_vmwrite(HOST_IA32_EFER, __readmsr(IA32_MSR_EFER));
__vmx_vmwrite(HOST_FS_BASE, __readmsr(IA32_FS_BASE));
__vmx_vmwrite(HOST_GS_BASE, __readmsr(IA32_GS_KERNEL_BASE));
__vmx_vmwrite(HOST_IA32_SYSENTER_CS, __readmsr(0x174));
__vmx_vmwrite(HOST_IA32_SYSENTER_ESP, __readmsr(0x175));
__vmx_vmwrite(HOST_IA32_SYSENTER_EIP, __readmsr(0x176));
//IDT GDT
GdtTable idtTable;
__sidt(&idtTable);
__vmx_vmwrite(HOST_GDTR_BASE, gdtTable.Base);
__vmx_vmwrite(HOST_IDTR_BASE, idtTable.Base);
}
参考文献
intel白皮书
邓志《处理器虚拟化技术》
B站周壑VT教学视频
https://github.com/qq1045551070/VtToMe
火哥上课讲的内容
看雪ID:smallzhong_
https://bbs.pediy.com/user-home-899076.htm
# 往期推荐
1.通过DWARF Expression将代码隐藏在栈展开过程中
2.x86-页式管理
6.Windows本地提权漏洞CVE-2014-1767分析及EXP编写指导
球分享
球点赞
球在看
点击“阅读原文”,了解更多!